package com.my.swas.sso.common.shiro;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.my.swas.common.jwt.JWTToken;
import com.my.swas.common.jwt.JWTUtils;
import com.my.swas.common.constans.CommConst;
import com.my.swas.sso.module.admin.entity.Menu;
import com.my.swas.sso.module.admin.entity.Role;
import com.my.swas.sso.module.admin.entity.User;
import com.my.swas.sso.module.admin.service.MenuService;
import com.my.swas.sso.module.admin.service.UserService;
import com.my.swas.sso.module.admin.vo.UserVo;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.mgt.RealmSecurityManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.crazycake.shiro.RedisSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description:
 * @author: :MaYong
 */
@Service
public class MyRealm extends AuthorizingRealm {

    private static final Logger logger = LoggerFactory.getLogger(MyRealm.class);
    @Resource
    private UserService userService;

    @Resource
    private MenuService resourcesService;

    @Autowired
    private RedisSessionDAO redisSessionDAO;



    /**
     * JWTToken 类型的Token，
     * 那么如果在StatelessAuthcFilter类中返回的是UsernamePasswordToken，那么将会报如下错误信息
     * 必须重写此方法，不然会报错
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JWTToken;
    }

    /**
     * 只有当需要检测用户权限的时候才会调用此方法，例如checkRole,checkPermission之类的
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //获取所有身份验证成功的Realm名字
        Set<String> realmNames = principalCollection.getRealmNames();
        //获取所有的身份信息集合
        Set principals = principalCollection.asSet();

        UserVo userVo  = userService.getUserVo(Long.valueOf(JWTUtils.getUserId(principalCollection.getPrimaryPrincipal().toString())));
        List<Menu> resourcesList = resourcesService.loadUserResources(userVo.getId());
        // 权限信息对象info,用来存放查出的用户的所有的角色（role）及权限（permission）
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //添加用户的资源权限
        info.addStringPermissions(resourcesList.stream().map(Menu:: getResUrl).collect(Collectors.toSet()));
        //添加用户的角色权限
        info.addRoles(userVo.getRoleList().stream().map(Role:: getRoleName).collect(Collectors.toSet()));
        return info;
    }

    //接口认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token){
        if (token.getCredentials() == null) {
            throw new AuthenticationException("token已失效");
        }
        String jwtToken = String.valueOf(token.getPrincipal());
        //获取用户的输入的账号.
        String userAccount = JWTUtils.getUserAccount(jwtToken);
        User user = new User();
        user.setUserAccount(userAccount);
        User dbUser = userService.getOne(new QueryWrapper<>(user));
        if(dbUser == null|| !JWTUtils.verify(jwtToken,userAccount,dbUser.getPassword(),dbUser.getId())){
            if(dbUser != null) {
                saveVerifyCount(dbUser.getId(), dbUser.getVerifyCount() + CommConst.ONE);
            }
            throw new AuthenticationException("请输入正确的用户名和密码");
        }

        if (CommConst.NO.equals(dbUser.getEnable())) {
            throw new LockedAccountException("您的账户已经锁定，请联系管理员");
        }
        if(CommConst.FIVE.compareTo(dbUser.getVerifyCount()) <= 0){
            throw new ExcessiveAttemptsException("验证未通过错误次数过多,帐号已被锁定，请联系管理员");
        }
        //参数依次：身份，凭据，自定义Realm的名称(可以随意取)
        //身份可以使用任意可以表示用户的唯一标识，该处使用jwtToken,可以唯一标识用户(第一个参数是缓存的key，一定不能重复)
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(jwtToken, jwtToken,getName());
        //验证通过以后把验证次数清零
        saveVerifyCount(dbUser.getId(), CommConst.ZERO);
        return authenticationInfo;
    }

    private void saveVerifyCount(Long userId,Integer verifyCount){
        User userVerifyCount = new User();
        userVerifyCount.setId(userId);
        userVerifyCount.setVerifyCount(verifyCount);
        userService.updateVerifyCount(userVerifyCount);
    }


    /**
     * 根据userId 清除当前session存在的用户的权限缓存
     *
     * @param userIds 已经修改了权限的userId
     */
    public void clearUserAuthByUserId(List<Long> userIds) {
        if (null == userIds || userIds.size() == 0) return;
        //获取所有session
        Collection<Session> sessions = redisSessionDAO.getActiveSessions();
        //定义返回
        List<SimplePrincipalCollection> list = new ArrayList<>();
        for (Session session : sessions) {
            //获取session登录信息。
            Object obj = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
            if (null != obj && obj instanceof SimplePrincipalCollection) {
                //强转
                SimplePrincipalCollection spc = (SimplePrincipalCollection) obj;
                Long userId  = JWTUtils.getUserId(String.valueOf(spc.getPrimaryPrincipal()));
                //判断用户，匹配用户ID。
                if(userIds.contains(userId)){
                    list.add(spc);
                }
            }
        }
        RealmSecurityManager securityManager =
                (RealmSecurityManager) SecurityUtils.getSecurityManager();
        MyRealm realm = (MyRealm) securityManager.getRealms().iterator().next();
        for (SimplePrincipalCollection simplePrincipalCollection : list) {
            realm.clearCachedAuthorizationInfo(simplePrincipalCollection);
        }
    }

    /**
     * 负责擦除认证信息
     * @param principals
     */
    @Override
    protected void clearCachedAuthenticationInfo(PrincipalCollection principals) {
        Cache c = getAuthenticationCache();
        //清除【认证】缓存之前
        for(Object o : c.keys()){
            logger.info( o + " , " + c.get(o));
        }
        super.clearCachedAuthenticationInfo(principals);
        logger.info("调用父类清除【认证】缓存之后");
        for(Object o : c.keys()){
            logger.info( o + " , " + c.get(o));
        }

        // 添加下面的代码清空【认证】的缓存
        SimplePrincipalCollection spc = new SimplePrincipalCollection(principals.getPrimaryPrincipal(),getName());
        super.clearCachedAuthenticationInfo(spc);
        logger.info("添加了代码清除【认证】缓存之后");
        int cacheSize = c.keys().size();
        logger.info("【认证】缓存的大小:" + c.keys().size());
        if (cacheSize == 0){
            logger.info("说明【认证】缓存被清空了。");
        }
    }

    /**
     * 负责擦除权限信息
     * @param principals
     */
    @Override
    protected void clearCachedAuthorizationInfo(PrincipalCollection principals) {
        //清除【授权】缓存之前
        Cache c = getAuthorizationCache();
        for(Object o : c.keys()){
            logger.info( o + " , " + c.get(o));
        }
        super.clearCachedAuthorizationInfo(principals);
        logger.info("清除【授权】缓存之后");
        int cacheSize = c.keys().size();
        logger.info("【授权】缓存的大小:" + cacheSize);

        for(Object o : c.keys()){
            logger.info( o + " , " + c.get(o));
        }
        if(cacheSize == 0){
            logger.info("说明【授权】缓存被清空了。");
        }
    }
}